import logging
import os
import re
import sys
import tarfile
import zipfile
from abc import ABC, abstractmethod
from pathlib import Path
from typing import List, Dict
from bs4 import BeautifulSoup
from datetime import timezone, timedelta, datetime

BASE_DIR = Path(__file__).absolute().parent
BIN_DIR = Path(sys.argv[0]).absolute().parent


class BaseChecker(ABC):
    LOG_PREFIX = ""
    PROJECT_NAME = ""
    MUST_FEATURES = []

    def __init__(self):
        if not self.PROJECT_NAME:
            raise Exception("PROJECT_NAME not defined")

    def check(self, log_files: List[str]) -> (list, bool):
        """判断各特性使能情况"""
        files = self._filter_log(log_files)
        if not files:
            return [], False

        feature_dict = self._check(files)
        return self._analyse(feature_dict)

    def _filter_log(self, log_files: list) -> list:
        """过滤出当前特性对应的采集日志"""
        if not self.LOG_PREFIX:
            raise Exception('LOG_PREFIX not defined')
        return list(filter(lambda x: Path(x).name.startswith(self.LOG_PREFIX), log_files))

    @abstractmethod
    def _check(self, log_files: list) -> Dict[str, dict]:
        """检查各特性使能情况"""
        pass

    def _analyse(self, feature_dict: Dict[str, dict]) -> (list, bool):
        """根据各特性使能情况得出使能结果"""
        features = []
        for feature, info in feature_dict.items():
            features.append({
                'name': feature,
                'desc': '必选' if feature in self.MUST_FEATURES else '非必选',
                'status': info['status'],
                'features': info['features'],
            })

        if self.MUST_FEATURES:
            enabled = all([feature_dict[k]['status'] for k in self.MUST_FEATURES])
        else:
            enabled = any([v['status'] for v in feature_dict.values()])
        return features, enabled

    @staticmethod
    def _count_true(feature_dict: Dict[str, bool], keys: List[str]) -> int:
        return sum([feature_dict[k] for k in keys])


class BishengJDKChecker(BaseChecker):
    LOG_PREFIX = "bisheng_jdk"
    PROJECT_NAME = "毕昇JDK"

    def _check(self, log_files: list):
        heap, jbooster, jbolt, kae_provider, kae_zlib, g1gc = False, False, False, False, False, False
        for per_log in log_files:
            name = Path(per_log).name
            if name == "bisheng_jdk_heap.log":
                heap = self._check_bisheng_jdk_heap(per_log)
            elif name == "bisheng_jdk_jbooster.log":
                jbooster = self._check_bisheng_jdk_jbooster(per_log)
            elif name == "bisheng_jdk_jbolt.log":
                jbolt = self._check_bisheng_jdk_jbolt(per_log)
            elif name == "bisheng_jdk_kae_provider.log":
                kae_provider = self._check_bisheng_jdk_kae_provider(per_log)
            elif name == "bisheng_jdk_kae_zlib.log":
                kae_zlib = self._check_bisheng_jdk_kae_zlib(per_log)
            elif name == "bisheng_jdk_g1gc.log":
                g1gc = self._check_bisheng_jdk_g1gc(per_log)

        features = {
            '分布式JIT编译': jbooster,
            'Jbolt': jbolt,
            'KAE Provider': kae_provider,
            'KAE-ZIP': kae_zlib,
            'G1GC优化': g1gc,
        }

        return {
            "毕昇JDK": {
                "status": any(features.values()),
                "features": features,
            },
        }

    def _check_bisheng_jdk_heap(self, log_path):
        with open(log_path, 'r', encoding='utf-8') as f:
            return 'HeapDump Redact Level = NAMES' in f.read()

    def _check_bisheng_jdk_jbooster(self, log_path):
        with open(log_path, 'r', encoding='utf-8') as f:
            return 'JBooster' in f.read()

    def _check_bisheng_jdk_jbolt(self, log_path):
        with open(log_path, 'r', encoding='utf-8') as f:
            return 'JBolt' in f.read()

    def _check_bisheng_jdk_kae_provider(self, log_path):
        with open(log_path, 'r', encoding='utf-8') as f:
            return 'KAEProvider' in f.read()

    def _check_bisheng_jdk_kae_zlib(self, log_path):
        with open(log_path, 'r', encoding='utf-8') as f:
            return '/usr/local/kaezip/lib/libz.so' in f.read()

    def _check_bisheng_jdk_g1gc(self, log_path):
        with open(log_path, 'r', encoding='utf-8') as f:
            return 'G1GC' in f.read()


class IsvTool:
    def __init__(self, tool: str, output=None):
        self.tool = tool
        self.output = output
        self.result = {
            "Devkit工具": {
                "编译构建": {
                    "毕昇JDK": [
                        {"KAE Provider": "不通过"},
                        {"KAE-ZIP": "不通过"},
                        {"Jbolt": "不通过"},
                        {"分布式JIT编译": "不通过"},
                        {"G1GC优化": "不通过"}
                    ]
                }
            },
        }

        if tool == 'BishengJDK':
            self.checker_dict: Dict[str, BaseChecker] = {
                "bisheng_jdk": BishengJDKChecker(),
            }
        else:
            raise Exception(f'Unsupported tool [{tool}]')

        self.default_html = os.path.join(os.getcwd(), "report.html")

    def analysis(self, log_file_list):
        index = 1
        feature_result = []
        project_success = []
        project_failed = []
        for project, checker in self.checker_dict.items():
            feature_list, enabled = checker.check(log_file_list)
            if not feature_list:
                continue

            project_name = checker.PROJECT_NAME
            if enabled:
                project_success.append(project_name)
            else:
                project_failed.append(project_name)

            for f in feature_list:
                feature_str = '\n'.join([f'{k}: {"使能" if v else "未使能"}' for k, v in f['features'].items()])
                feature_result.append([index, project_name, f['name'], feature_str, "通过" if f['status'] else "失败"])
                index += 1

        # 转化使能评估结果适用于html 数据填充
        if not project_success and project_failed:
            enable_list = [{"label": "使能评估失败", "value": "{} 【失败】".format(" ".join(project_failed))}]
        elif project_success and not project_failed:
            enable_list = [{"label": "使能评估成功", "value": "{} 【通过】".format(" ".join(project_success))}]
        elif not project_success and not project_failed:
            enable_list = [{"label": "使能评估结果", "value": "未使能任何解决方案"}]
        else:
            enable_list = [
                {"label": "使能评估成功", "value": "{} 【通过】".format(" ".join(project_success))},
                {"label": "使能评估失败", "value": "{} 【失败】".format(" ".join(project_failed))},
            ]

        return feature_result, len(project_success), enable_list

    @staticmethod
    def decompress_zip(compressed_report_package):
        with zipfile.ZipFile(compressed_report_package) as zip:
            zip.extractall("./")
            file_names = zip.namelist()
            return file_names

    @staticmethod
    def decompress_gz_bz(compressed_report_package):
        with tarfile.open(compressed_report_package, "r", encoding='utf-8') as tar:
            tar.extractall(path="./")
            file_names = tar.getnames()
            return file_names

    def generator_html(self, new_html_path, feature_num, config_dict, feature_msg):
        template = """
                <html>
                <head>
                <meta charset="utf-8"/>
                <meta content="IE=edge" http-equiv="X-UA-Compatible"/>
                <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
                <title>Document</title>
                </head>
                <body>
                <div class="header">
                      鲲鹏使能套件{{tool_name}}验收报告
                    </div>
                <div class="content">
                <div class="title">
                        配置信息
                      </div>
                <div class="info">
                <div class="left">
                <div class="left-content">
                </div>
                </div>
                <div class="right">
                <div class="text">已使能解决方案</div>
                <div class="num">1</div>
                </div>
                </div>
                <div class="table">
                <div class="title">验收详细结果</div>
                <table>
                <tr>
                <th>序号</th>
                <th>解决方案</th>
                <th>特性</th>
                <th>子特性结果</th>
                <th>分析结果</th>
                </tr>
                </table>
                </div>
                </div>
                </body>
                <script>    
                
                
                
                    // 配置信息
                    var leftContentEle = document.getElementsByClassName('left-content')[0];
                    infoList.forEach(item => {
                      let singleEle = document.createElement('div');
                      singleEle.setAttribute('class', 'single');
                      let labelEle = document.createElement('div');
                      labelEle.setAttribute('class', 'label');
                      labelEle.innerText = item.label;
                      let valueEle = document.createElement('div');
                      valueEle.setAttribute('class', 'value');
                      valueEle.innerText = item.value;
                
                      singleEle.appendChild(labelEle);
                      singleEle.appendChild(valueEle);
                      leftContentEle.appendChild(singleEle);
                    })
                
                
                    // 已使能特性
                    var numEle = document.getElementsByClassName('num')[0];
                    numEle.innerText = num;
                
                
                    // 验收详细结果
                    var tableEle = document.getElementsByTagName('table')[0];
                    tableList.forEach(list => {
                      let trEle = document.createElement('tr');
                      list.forEach(item => {
                        let tdEle = document.createElement('td');
                        tdEle.innerText = item;
                        trEle.appendChild(tdEle);
                      })
                      tableEle.appendChild(trEle);
                    })
                
                
                  </script>
                <style>
                    body {
                      background-color: #222020;
                      color: #fff;
                      padding: 8px 16px;
                      margin: 0;
                      font-size: 14px;
                    }
                    .header {
                      font-size: 18px;
                      width: 100%;
                      line-height: 50px;
                      border-bottom: 1px solid rgb(110, 110, 110);
                    }
                    .info {
                      display: flex;
                      justify-content: flex-start;
                      align-items: flex-start;
                    }
                    .left {
                      width: 60%;
                    }
                    .title {
                      font-size: 16px;
                      margin: 20px 0 10px 0;
                    }
                    .single {
                      display: flex;
                      justify-content: flex-start;
                      align-items: center;
                      margin: 8px 0;
                    }
                    .label {
                      color: rgb(195, 206, 216);
                      width: 30%;
                    }
                    .right {
                      width: 40%;
                      text-align: center;
                    }
                    .text {
                      font-size: 20px;
                      color: rgb(195, 206, 216);
                    }
                    .num {
                      font-size: 50px;
                    }
                    table {
                      border: 1px solid rgb(110, 110, 110);
                      width: 100%
                    }
                    th{
                      font-size: 14px; 
                      padding: 5px 15px;
                      color: rgb(222, 227, 231);
                      background-color: #555353;
                    }
                    td {
                      padding: 2px 5px;
                      border-top: 1px solid rgb(110, 110, 110);
                      border-right: 1px solid rgb(110, 110, 110);
                      text-align: center;
                    }
                    td:last-child {
                      border-right: none
                
                    }
                  </style>
                </html>"""
        template = template.replace('{{tool_name}}', self.tool)
        soup = BeautifulSoup(template, "html.parser")
        aa = soup.find_all("script")
        temp = aa[0].string
        format_str = """    
    var infoList = {};

    // 已使能特性
    var num = {};   

    // 验收详细结果
    var tableList = {}; 
    {}"""
        if feature_msg:
            detail_info = feature_msg[0][3].split("\n")
            self.result["Devkit工具"]["编译构建"]["毕昇JDK"][3]["分布式JIT编译"] = "通过" if detail_info[0].split(": ")[
                                                                                                 1] == "使能" else "不通过"
            self.result["Devkit工具"]["编译构建"]["毕昇JDK"][2]["Jbolt"] = "通过" if detail_info[1].split(": ")[
                                                                                         1] == "使能" else "不通过"
            self.result["Devkit工具"]["编译构建"]["毕昇JDK"][0]["KAE Provider"] = "通过" if detail_info[2].split(": ")[
                                                                                                1] == "使能" else "不通过"
            self.result["Devkit工具"]["编译构建"]["毕昇JDK"][1]["KAE-ZIP"] = "通过" if detail_info[3].split(": ")[
                                                                                           1] == "使能" else "不通过"
            self.result["Devkit工具"]["编译构建"]["毕昇JDK"][4]["G1GC优化"] = "通过" if detail_info[4].split(": ")[
                                                                                            1] == "使能" else "不通过"
        insert_str = format_str.format(
            str(config_dict),
            str(feature_num),
            str(feature_msg),
            temp
        )
        aa[0].string = insert_str
        if not self.output:
            with open(new_html_path, "w", encoding="UTF-8") as f:
                f.write(str(soup))
            print("Decompression completed. View the report in {}.".format(os.getcwd()))
        else:
            print(self.result)

    def acquire_os_msg(self, file_list):
        log_path = None
        for f in file_list:
            if Path(f).name.startswith("os_msg"):
                log_path = f
                break

        if not log_path:
            insert_list = [
                {"label": "报告生成时间",
                 "value": datetime.now(tz=timezone(timedelta(hours=8))).strftime("%Y/%m/%d %H:%M:%S")},
                {"label": "操作系统版本", "value": "未获取"},
                {"label": "内核版本", "value": "未获取"},
                {"label": "鲲鹏服务器的机型", "value": "未获取"},
                {"label": "工具路径", "value": str(BIN_DIR)},
                {"label": "工具版本", "value": "1.1"},
            ]
            return insert_list

        os_dict = {
            "os": "None",
            "version": "None",
            "kernel": "None",
            "product_name": "None",
        }
        with open(log_path, 'r', encoding="utf-8") as f:
            for per_line in f.readlines():
                per_line = per_line.strip()
                if per_line.startswith("os:"):
                    os_re = re.compile(r"os: (.*)").findall(per_line.strip())
                    os_dict["os"] = os_re[0].strip("\"") if os_re else "None"
                elif per_line.startswith("Version:"):
                    version_re = re.compile(r"Version:(.*)").findall(per_line)
                    os_dict["version"] = version_re[0] if version_re else "None"
                elif per_line.startswith("kernel:"):
                    kernel = re.compile(r"kernel: (.*)").findall(per_line)
                    os_dict["kernel"] = kernel[0] if kernel else "None"
                elif per_line.startswith("Product Name:"):
                    product_name_re = re.compile(r"Product Name:(.*)").findall(per_line)
                    os_dict["product_name"] = product_name_re[0] if product_name_re else "None"
                elif per_line.startswith("customer_information:"):
                    customer_information_re = re.compile(r"customer_information:(.*)").findall(per_line)
                    os_dict["customer_information"] = customer_information_re[0] if customer_information_re else "None"

        insert_list = [
            {"label": "报告生成时间",
             "value": datetime.now(tz=timezone(timedelta(hours=8))).strftime("%Y/%m/%d %H:%M:%S")},
            {"label": "操作系统版本", "value": os_dict.get("os", "未获取")},
            {"label": "内核版本", "value": os_dict.get("kernel", "未获取")},
            {"label": "鲲鹏服务器的机型", "value": os_dict.get("product_name", "未获取")},
            {"label": "鲲鹏cpu型号", "value": os_dict.get("version", "未获取")},
            {"label": "信息收集压缩包名称", "value": os_dict.get("customer_information", "客户未填写")},
            {"label": "工具路径", "value": str(BIN_DIR)},
            {"label": "工具版本", "value": "1.1"},
        ]
        return insert_list
